﻿/******************************************************
* author :  cwj
* email  :  chenwenji_360@live.com 
* history:  created by cwj 2015/8/19 13:41:52 
* clrversion :4.0.30319.42000
******************************************************/

using Machine.DataAccess.Linq;
using Machine.DataAccess.Operation;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;

namespace Machine.DataAccess.Common.ORM
{
    class MySqlSchemInfo : DbSchemInfoBase
    {
        public MySqlSchemInfo(ProviderElement providerElement) : base(providerElement) { }

        private static readonly string GET_PRIMARY_KEY = "SELECT t.TABLE_NAME as 'TableName', c.COLUMN_NAME as 'CloumName' FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS t, INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS c WHERE t.TABLE_NAME = c.TABLE_NAME AND t.CONSTRAINT_TYPE = 'PRIMARY KEY' AND t.TABLE_NAME = '{0}' AND t.TABLE_SCHEMA = '{1}'";
        private static readonly string GET_NOMAL_COLUNM = "SELECT t.COLUMN_NAME, t.COLUMN_KEY FROM information_schema. COLUMNS as t WHERE table_name = '{0}' AND t.COLUMN_KEY = '' AND t.TABLE_SCHEMA='{1}'";
        private static Dictionary<string, string> primaryKeyCache = new Dictionary<string, string>();
        public override string GetPrimaryKey(string tableName)
        {
            if (!primaryKeyCache.ContainsKey(tableName))
            {
                tableName = tableName.ToLower();
                var sql = string.Format(GET_PRIMARY_KEY, tableName,this.ProviderElement.Key);
                var colums = new DataReader<PrimaryKeyTemp>(new TranslateResult(sql, new List<DbParameter>()), this.ProviderElement,null).ToList();
                if (colums.Count == 0) return null;
                primaryKeyCache[tableName] = colums.First(x=>x.TableName == tableName).CloumName.ToLower();
            }
            return primaryKeyCache[tableName];
        }

        public override bool GetPrimaryKeyType(string tableName)
        {
            return false;
        }

        private static readonly string GET_FK = "SELECT t.REFERENCED_TABLE_NAME AS 'table', t.COLUMN_NAME AS 'from', t.REFERENCED_COLUMN_NAME AS 'to' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS t WHERE t.TABLE_NAME = '{0}' AND t.TABLE_SCHEMA = '{1}' AND t.CONSTRAINT_NAME <> 'primary'";
        private static ConcurrentDictionary<string, Dictionary<string, DbForeignKeyInfo[]>> fkCache = new ConcurrentDictionary<string, Dictionary<string, DbForeignKeyInfo[]>>();
        public override DbForeignKeyInfo[] GetForeignKey(string tableName)
        {
            if (!fkCache.ContainsKey(ProviderElement.Key)) fkCache[ProviderElement.Key] = new Dictionary<string, DbForeignKeyInfo[]>();
            if (!fkCache[ProviderElement.Key].ContainsKey(tableName) || fkCache[ProviderElement.Key][tableName] == null)
            {
                var sql = string.Format(GET_FK, tableName, ProviderElement.Key);
                fkCache[ProviderElement.Key][tableName] = new DataReader<DbForeignKeyInfo>(new TranslateResult(sql, new DbParameter[0]), this.ProviderElement, null).ToArray();
            }
            return fkCache[ProviderElement.Key][tableName];
            //var sql = string.Format(GET_FK, tableName, ProviderElement.Key);
            //return new DataReader<DbForeignKeyInfo>(new TranslateResult(sql, new DbParameter[0]), this.ProviderElement, null).ToArray();
        }

        protected override void RemoveFkCache(string type)
        {
            fkCache[ProviderElement.Key][type] = null;
        }

        private static readonly string GET_ALLTABLE = "Show Tables";
        public override string[] GetAllTables()
        {
            return new DataReader<string>(new TranslateResult(GET_ALLTABLE, new DbParameter[0]), this.ProviderElement,null).ToArray();
        }

        public override bool ExtistTable(string name)
        {
            if (!TableCache.ContainsKey(ProviderElement.Key) || !TableCache[ProviderElement.Key].Contains(name))
            {
                if (!TableCache.ContainsKey(ProviderElement.Key)) TableCache[ProviderElement.Key] = new ConcurrentSet<string>();
                foreach (var item in this.GetAllTables())
                {
                    TableCache[ProviderElement.Key].Add(item);
                }
            }

            return TableCache[ProviderElement.Key].Contains(name);
            //return new DataReader<string>(new TranslateResult(GET_ALLTABLE, new DbParameter[0]), this.ProviderElement,null).FirstOrDefault(x => x == name) != null;
        }

        public override Tuple<string, DbForeignKeyInfo[]> GetOuterFKey(Type bigType, Type collectionType)
        {
            var bigTypeName = bigType.GetTableName();
            var collectionTypeName = collectionType.GetTableName();

            //var relation = this.GetTableRelationType(bigType, collectionType);
            //DbForeignKeyInfo[] outterKey = null;
            //switch (relation)
            //{
            //    case TableRelationType.Zero2Zero:
            //        return null;
            //    case TableRelationType.Zero2One:
            //        break;
            //    case TableRelationType.Zero2Many:
            //        break;
            //    case TableRelationType.One2Zero:
            //        break;
            //    case TableRelationType.One2One:
            //        break;
            //    case TableRelationType.One2Many:
            //        break;
            //    case TableRelationType.Many2Zero:
            //        outterKey = this.GetForeignKey(bigTypeName);
            //        break;
            //    case TableRelationType.Many2One:
            //        outterKey = this.GetForeignKey(bigTypeName);
            //        return new Tuple<string,DbForeignKeyInfo[]>(collectionTypeName,)
            //        break;
            //    case TableRelationType.Many2Many:
            //        break;
            //    default:
            //        break;
            //}

            var outerFk = this.GetForeignKey(collectionTypeName).ToDictionary(x => x.Table);
            if (outerFk.ContainsKey(bigTypeName)) return new Tuple<string, DbForeignKeyInfo[]>(collectionTypeName, new DbForeignKeyInfo[] { outerFk[bigTypeName] });
            outerFk = this.GetForeignKey(bigTypeName).ToDictionary(x => x.Table);
            if (outerFk.ContainsKey(collectionTypeName)) return new Tuple<string, DbForeignKeyInfo[]>(bigTypeName, new DbForeignKeyInfo[] { outerFk[collectionTypeName] });

            var tableNames = this.GetAllTables();
            foreach (var tableName in tableNames)
            {
                this.GetForeignKey(tableName);
            }

            foreach (var item in fkCache[ProviderElement.Key])
            {
                var fkDic = item.Value.ToDictionary(x => x.Table);
                if (fkDic.ContainsKey(bigTypeName) && fkDic.ContainsKey(collectionTypeName))
                    return new Tuple<string, DbForeignKeyInfo[]>(item.Key,
                        new DbForeignKeyInfo[] { fkDic[bigTypeName], fkDic[collectionTypeName] });
            }
            return null;
        }

        protected override ConcurrentSet<string> GetTableColums(string tableName)
        {
            var sql = string.Format(GET_NOMAL_COLUNM, tableName, this.ProviderElement.Key);
            var temp = new DataReader<string>(new TranslateResult(sql, new DbParameter[0]), this.ProviderElement, null).ToArray();
            var set = new ConcurrentSet<string>();
            foreach (var item in temp)
            {
                set.Add(item.ToLower());
            }
            return set;
        }


        private class PrimaryKeyTemp
        {
            public string TableName { get; set; }
            public string CloumName { get; set; }
        }

        private class TableTemp
        {
            public string Name { get; set; }
        }
    }
}
